﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using Microsoft.ServiceBus;
using nwrole.lib;

namespace nwrole.lib2 {
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class Supervisor : ISupervisorContract {
        public static string Identifier = "nwrole.SupervisorService";
        public static string ServiceDisplayName = "nwrole Supervisor";
        public static string ApplicationDirectory = "nwrole";

        /*public static IEnumerable GetAllClasses(string nameSpace) {
            Assembly asm = Assembly.GetExecutingAssembly(); 
            if (!String.IsNullOrWhiteSpace(nameSpace)) 
                return asm.GetTypes().Where(x => x.Namespace == nameSpace).Select(x => x.Name); 
            return asm.GetTypes().Select(x => x.Name);
        }

        public static IEnumerable GetAllClassesOfNamespace(Assembly assembly, string @namespace) {
            return (from type in assembly.GetTypes() where type.Namespace == @namespace select type.Name).OrderBy(type => type);
        }*/

        private IOrchestratorChannel orchestratorChannel;
        private int SupervisorPort;

        private Timer masterConnectionTimer = new Timer();

        private List<ServiceData> ServiceDatas = new List<ServiceData>();

        public Supervisor(int supervisorPort) {
            SupervisorPort = supervisorPort;

            masterConnectionTimer.Elapsed += OnMasterConnectionTimer;
            masterConnectionTimer.Interval = 60 * 1000;
        }

        #region Supervisor
        public void Start() {
            WriteLog("Start");
            EventLogger.LogInformation("Supervisor", "Supervisor starting");
            try {
                Configuration.Load();

                string servicesDir = Configuration.ServicesDirectory;
                if (!Directory.Exists(servicesDir))
                    Directory.CreateDirectory(servicesDir);

                foreach (string serviceDir in Directory.EnumerateDirectories(servicesDir)) {
                    ServiceData serviceData = null;

                    string configPath = Path.Combine(serviceDir, "service-config.xml");

                    try {
                        if (File.Exists(configPath)) {
                            PropertyList plist = new PropertyList(configPath);
                            serviceData = StorageUtil.ServiceDataFromDictionary((IDictionary)plist.DictionaryForKey("Service"));
                        }
                    } catch (Exception e) {
                        serviceData = null;
                    }

                    if (serviceData == null) {
                        serviceData = new ServiceData();
                        serviceData.Name = Path.GetFileName(serviceDir);
                        serviceData.Id = Guid.NewGuid();
                        serviceData.Enabled = false;
                        //serviceData.PluginDatas = new List<PluginData>();

                        PropertyList plist = new PropertyList();
                        plist["Service"] = StorageUtil.ServiceDataToDictionary(serviceData);

                        plist.Save(configPath);
                    }

                    serviceData.ManagementPort = NextFreeManagementPort();

                    ServiceDatas.Add(serviceData);
                }

                try {
                    ConnectToOrchestrator();
                } catch(Exception e) {
                    
                }

                foreach(var serviceData in ServiceDatas) {
                    if (serviceData.Enabled)
                        DeployUtil.DeployService(serviceData, true, SupervisorPort);
                }
            } catch (Exception exception) {
                EventLogger.LogException("Supervisor.Start", exception);
            }
            EventLogger.LogInformation("Supervisor", "Supervisor started");
        }

        public void Stop() {
            EventLogger.LogInformation("Supervisor", "Supervisor stopping");
            foreach (var serviceData in ServiceDatas) {
                try {
                    if (serviceData.Deployed)
                        DeployUtil.RetractService(serviceData, true);
                } catch (Exception exception) {
                    EventLogger.LogException("Supervisor.Stop", exception);
                }
            }
            
            EventLogger.LogInformation("Supervisor", "Supervisor stopped");
        }
        #endregion

        #region ISupervisorContract
        public void StartService() {
            
        }

        public void StopService() {

        }

        public int GetNumServices() {
            return ServiceDatas.Count;
        }

        public ServiceData[] ListServices() {
            return ServiceDatas.ToArray();
        }

        public void ReportError(string message) {
            WriteLog("Supervisor.ReportError: " + message);
            if (orchestratorChannel != null)
                orchestratorChannel.ReportError(message);
        }

        private static void WriteLog(string content) {
            DateTime now = DateTime.Now;
            var path = "c:/log/nwrole/Supervisor/";
            if (!System.IO.Directory.Exists(path))
                System.IO.Directory.CreateDirectory(path);

            System.IO.File.AppendAllText(path + "log " + now.ToString("yyyy-MM-dd") + ".txt", now.ToString("yyyy-MM-dd hh:mm:ss ") + content + "\r\n");
        }

        #endregion

        public void ConnectToOrchestrator() {
            try {
                ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;

                Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", "systemevolutiontestfive",
                                                                        "nwrole/Orchestrator");

                //Console.WriteLine("Connecting to {0} address: {1}", DeployContext.Config.DeployName, serviceUri);

                TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
                sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
                sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = "owner";
                sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret =
                    "ihXdEKIJtg8jByN1aITLDX99eiEgMeHOfr4ppzX5MfM=";

                //ChannelFactory<IOrchestratorChannel> channelFactory = new ChannelFactory<IOrchestratorChannel>();
                DuplexChannelFactory<IOrchestratorChannel> channelFactory =
                    new DuplexChannelFactory<IOrchestratorChannel>(new OrchestratorClient(this));
                channelFactory.Endpoint.Address = new EndpointAddress(serviceUri);
                channelFactory.Endpoint.Binding = new NetTcpRelayBinding();
                channelFactory.Endpoint.Contract.ContractType = typeof(IOrchestratorChannel);

                channelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);

                orchestratorChannel = channelFactory.CreateChannel();
                orchestratorChannel.Open();

                orchestratorChannel.RegisterSupervisor();
            }
            catch(Exception e)
            {
                orchestratorChannel = null;
            }

            if (orchestratorChannel == null) {
                
                masterConnectionTimer.Start();
            }
        }

        private void OnMasterConnectionTimer(object source, ElapsedEventArgs e) {
            masterConnectionTimer.Stop();
            ConnectToOrchestrator();
        }

        #region Service management
        public static bool IsInstalled {
            get {
                ServiceController ctrl = new ServiceController(Identifier);
                try {
                    ServiceControllerStatus status = ctrl.Status;
                    return true;
                } catch (Exception) { return false; } finally { ctrl.Dispose(); }
            }
        }

        public static bool IsRunning {
            get {
                if (!IsInstalled)
                    return false;
                ServiceController ctrl = new ServiceController(Identifier);
                try { return ctrl.Status == ServiceControllerStatus.Running; } finally { if (ctrl != null) ctrl.Dispose(); }
            }
        }

        public static void StartSupervisor() {
            if (!IsInstalled)
                return;
            ServiceController ctrl = new ServiceController(Identifier);
            try {
                ctrl.Start();
                ctrl.WaitForStatus(ServiceControllerStatus.Running);
            } finally { if (ctrl != null) ctrl.Dispose(); }
        }

        public static void StopSupervisor() {
            if (!IsRunning)
                return;
            ServiceController ctrl = new ServiceController(Identifier);
            try {
                ctrl.Stop();
                ctrl.WaitForStatus(ServiceControllerStatus.Stopped);
            } finally { if (ctrl != null) ctrl.Dispose(); }
        }

        /*public static void InstallSupervisor(string username, string password) {
            string rootFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), ApplicationDirectory);

            UpdateSupervisor();

            ServiceUtil.InstallService(Path.Combine(rootFolder, Configuration.ApplicationExe) + " supervisor", Identifier, ServiceDisplayName, username, password);

            EventLogger.LogInformation("SupervisorService installed");
        }

        public static void UpdateSupervisor() {
            string rootFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), ApplicationDirectory);

            if (!Directory.Exists(rootFolder))
                Directory.CreateDirectory(rootFolder);

            // Metod 1
            //foreach (AssemblyName assemblyName in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
            //{
            //    Assembly assembly = Assembly.Load(assemblyName);
            //    if (!assembly.GlobalAssemblyCache)
            //    {
            //        string name = Path.GetFileName(assembly.Location);
            //        File.Copy(assembly.Location, Path.Combine(rootFolder, name), true);
            //    }
            //}

            // Metod 2
            // copy the needed dll's
            foreach (string name in new string[] { "nwrole.lib.dll", "nwrole.lib2.dll", "nwrole.plugin.dll", "Microsoft.ServiceBus.dll", "nwrole.service.exe", "ICSharpCode.SharpZipLib.dll" }) {
                string source = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), name);
                File.Copy(source, Path.Combine(rootFolder, name), true);
            }
        }

        public static void UninstallSupervisor() {
            string rootFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), ApplicationDirectory);

            ServiceUtil.UninstallService(Identifier);

            if (Directory.Exists(rootFolder))
                Directory.Delete(rootFolder, true);

            EventLogger.LogInformation("SupervisorService uninstalled");
        }*/
        #endregion

        public int NextFreeManagementPort() {
            int port = 63000;
            bool success = true;
            do {
                port++;
                success = true;
                foreach (var serviceData in ServiceDatas) {
                    if (serviceData.ManagementPort == port) {
                        success = false;
                        break;
                    }
                }
            } while (!success);
            return port;
        }

    }
}
